//
//  environment.cpp
//  cobalt
//
//  Created by Nicolas Godbout on 2013-08-28.
//  Copyright (c) 2013 Nicolas Godbout. All rights reserved.
//

#include "environment.h"
#include "vocabulary.h"

#include <cassert>
#include <cstdio>
#include <iostream>
#include <utility>

namespace rdf {
    
    RDFNode const&
    Environment::createBlankNode() noexcept {
        // ## TODO plug the memory leak, make this at least less ugly
        char buffer[16];
        while ( true ) {
            int len = std::sprintf(buffer, "%lld", blankNodeCounter++);
            char* id = new char[len + 1];
            std::memcpy(id, buffer, len + 1);
            
            auto pair = indexBlank.insert(RDFNode(NodeRecord(str_BlankNode, id)));
            
            if ( pair.second )
                return *pair.first;
            else {
                delete id;
                continue;
            }
        }
    }
    
    RDFNode const&
    Environment::createBlankNode(const char* id) noexcept {
        auto pair = indexBlank.insert(RDFNode(NodeRecord(str_BlankNode, id)));
        if ( pair.second )
            return *pair.first;
        else {
            // ## TODO: scan string tail, increase the number until unique
//            delete id;
            return *pair.first;
        }
    }
    
    RDFNode const&
    Environment::createNamedNode(char const* uri) noexcept {
        auto pair = indexNamed.insert(RDFNode(NodeRecord(str_NamedNode, uri)));
        return *pair.first;
    }
    
    RDFNode const&
    Environment::createNamedNode(const std::string& value) noexcept {
        auto pair = indexNamed.insert(RDFNode(NodeRecord(str_NamedNode, value.c_str())));
        return *pair.first;
    }

    
    RDFNode const&
    Environment::createLiteral(char const* value, NamedNode const& datatype) noexcept {
        auto pair = indexLiteral.insert(RDFNode(NodeRecord(str_Literal, value, datatype)));
        return *pair.first;
    }
    
    RDFNode const&
    Environment::createLiteral(char const* value, char const* lang) noexcept {
        Literal node = RDFNode(NodeRecord(str_LangString, value, lang));
        auto pair = indexLiteral.insert(std::move(node));
        return *pair.first;
    }
    
    RDFNode const&
    Environment::createLiteral(const std::string& value) noexcept {
        return createLiteral(value.c_str(), vocab::xsd::string);
    }
    
    RDFNode const&
    Environment::createLiteral(const std::string& value, const std::string& lang) noexcept {
        return createLiteral(value.c_str(), lang.c_str());
    }
    
    RDFNode const&
    Environment::createLiteral(const std::string& value, const NamedNode& datatype) noexcept {
        return createLiteral(value.c_str(), datatype);
    }
    
    Triple
    Environment::createTriple(RDFNode const& subject,
                              RDFNode const& predicate,
                              RDFNode const& object) noexcept {
        return Triple({subject, predicate, object});
    }
    
    Graph
    Environment::createGraph(Graph::TripleList triples) noexcept {
        Graph ret{};
        ret.add(triples);
        return ret;
    }
    
    /*
    Node
    Environment::createBlankNode() noexcept {
        // build a textual id
        char buffer[16];
        std::sprintf(buffer, "%lld", blankNodeCounter++);
        size_t len = std::strlen(buffer);
        char* id = new char[len + 1];
        std::memcpy(id, buffer, len);
        id[len] = '\0';
        
        Node node (new internal::BlankNode(id));
        auto pair = indexBlank.insert({id, node});
        
        // ## CHECK for memory leak in case of already existing id
        return pair.first->second;
    }
    
    Node
    Environment::createBlankNode(const std::string& str) noexcept {
        size_t len = str.length();
        char* id = new char[str.length() + 1];
        str.copy(id, len);
        id[len] = '\0';
        
        Node node (new internal::BlankNode(id));
        auto pair = indexBlank.insert({id, node});
        
        return pair.first->second;
    }
    
    Node
    Environment::createNamedNode(const std::string& str) noexcept {
        auto pair = indexIRI.insert(Node(internal::NamedNode(str)));
        
        return *pair.first;
    }
    
    //  Per the RDF 1.1 specification, all Literals have a datatype.
    //        - so-called plain Literals carry the datatype xsd:string
    //        - language-tagged Literals carry the datatype rdf:langString
    //
    
    Node
    Environment::createLiteral(const std::string &value) noexcept {
        return createLiteral(value,
                             {},
                             std::static_pointer_cast<internal::NamedNode>(Node::xsd_string.node));
    }
    
    Node
    Environment::createLiteral(const std::string& value, const std::string& lang) noexcept {
        return createLiteral(value,
                             lang,
                             std::static_pointer_cast<internal::NamedNode>(Node::rdf_langString.node));
    }
    
    Node
    Environment::createLiteral(const std::string& value, const Node& datatype) {
        if ( datatype.interfaceName() != co::NamedNode )
            throw;
        
        return createLiteral(value,
                             {},
                             std::static_pointer_cast<internal::NamedNode>(datatype.node));
    }
    
    Node
    Environment::createLiteral(const std::string &value,
                               const std::string &lang,
                               std::shared_ptr<internal::NamedNode> datatype) {
        // Perform hash lookup
        internal::Literal literal(value, lang, datatype);
        size_t hash = literal.hash();
        auto range = indexLiteral.equal_range(hash);
        Node ret;
        while ( range.first != range.second ) {
            if ( range.first->second.asLiteral() == literal ) {
                ret = range.first->second;
                break;
            }
            else {
                std::cerr << "Hash collision! Two literals hash to " << hash
                << "\n    " << literal.toNT() << "\n    "
                << range.first->second.asLiteral().toNT() << "\n";
                range.first++;
                continue;
            }
        }
        if ( !ret ) {
            ret = Node(new internal::Literal(literal));
            indexLiteral.insert({hash, ret});
        }
        return ret;
    }
    
    Triple
    Environment::createTriple(const Node& subject, const Node& predicate, const Node& object) {
        const char *t1 = subject.interfaceName();
        const char *t2 = predicate.interfaceName();
        const char *t3 = object.interfaceName();
        assert (   (t1 == co::NamedNode || t1 == co::BlankNode)
                && (t2 == co::NamedNode)
                && (t3 == co::NamedNode || t3 == co::BlankNode || t3 == co::Literal) );
        return internal::TripleBase(subject, predicate, object);
    }
    
    Graph*
    Environment::createGraph(Graph::TripleList triples) {
        Graph* ret = new Graph();
        ret->add(triples);
        return ret;
    }
    */
    namespace iri {
        const char* rdf_XMLLiteral  = "http://www.w3.org/1999/02/22-rdf-syntax-ns#XMLLiteral";
        const char* rdf_Property    = "http://www.w3.org/1999/02/22-rdf-syntax-ns#Property";
        const char* rdf_type        = "http://www.w3.org/1999/02/22-rdf-syntax-ns#type";
        const char* rdf_Bag         = "http://www.w3.org/1999/02/22-rdf-syntax-ns#Bag";
        const char* rdf_Seq         = "http://www.w3.org/1999/02/22-rdf-syntax-ns#Seq";
        const char* rdf_Alt         = "http://www.w3.org/1999/02/22-rdf-syntax-ns#Alt";
        const char* rdf_List        = "http://www.w3.org/1999/02/22-rdf-syntax-ns#List";
        const char* rdf_first       = "http://www.w3.org/1999/02/22-rdf-syntax-ns#first";
        const char* rdf_rest        = "http://www.w3.org/1999/02/22-rdf-syntax-ns#rest";
        const char* rdf_nil         = "http://www.w3.org/1999/02/22-rdf-syntax-ns#nil";
        const char* rdf_Statement   = "http://www.w3.org/1999/02/22-rdf-syntax-ns#Statement";
        const char* rdf_subject     = "http://www.w3.org/1999/02/22-rdf-syntax-ns#subject";
        const char* rdf_predicate   = "http://www.w3.org/1999/02/22-rdf-syntax-ns#predicate";
        const char* rdf_object      = "http://www.w3.org/1999/02/22-rdf-syntax-ns#object";
        const char* rdf_value       = "http://www.w3.org/1999/02/22-rdf-syntax-ns#value";
        const char* rdf_langString  = "http://www.w3.org/1999/02/22-rdf-syntax-ns#langString";
        
        const char* rdfs_Resource   = "http://www.w3.org/2000/01/rdf-schema#Resource";
        const char* rdfs_Class      = "http://www.w3.org/2000/01/rdf-schema#Class";
        const char* rdfs_Literal    = "http://www.w3.org/2000/01/rdf-schema#Literal";
        const char* rdfs_Datatype   = "http://www.w3.org/2000/01/rdf-schema#Datatype";
        const char* rdfs_range      = "http://www.w3.org/2000/01/rdf-schema#range";
        const char* rdfs_domain     = "http://www.w3.org/2000/01/rdf-schema#domain";
        const char* rdfs_subClassOf     = "http://www.w3.org/2000/01/rdf-schema#subClassOf";
        const char* rdfs_subPropertyOf  = "http://www.w3.org/2000/01/rdf-schema#subPropertyOf";
        const char* rdfs_label      = "http://www.w3.org/2000/01/rdf-schema#label";
        const char* rdfs_comment    = "http://www.w3.org/2000/01/rdf-schema#comment";
        const char* rdfs_Container  = "http://www.w3.org/2000/01/rdf-schema#Container";
        const char* rdfs_ContainerMembershipProperty = "http://www.w3.org/2000/01/rdf-schema#ContainerMembershipProperty";
        const char* rdfs_member     = "http://www.w3.org/2000/01/rdf-schema#member";
        const char* rdfs_seeAlso    = "http://www.w3.org/2000/01/rdf-schema#seeAlso";
        const char* rdfs_isDefinedBy    = "http://www.w3.org/2000/01/rdf-schema#isDefinedBy";
        
        const char* xsd_string      = "http://www.w3.org/2001/XMLSchema#string";
        const char* xsd_boolean     = "http://www.w3.org/2001/XMLSchema#boolean";
        const char* xsd_decimal     = "http://www.w3.org/2001/XMLSchema#decimal";
        const char* xsd_integer     = "http://www.w3.org/2001/XMLSchema#integer";
        const char* xsd_double      = "http://www.w3.org/2001/XMLSchema#double";
        const char* xsd_float       = "http://www.w3.org/2001/XMLSchema#float";
        const char* xsd_date        = "http://www.w3.org/2001/XMLSchema#date";
        const char* xsd_time        = "http://www.w3.org/2001/XMLSchema#time";
        // some more related to date/time
        const char* xsd_byte        = "http://www.w3.org/2001/XMLSchema#byte";
        const char* xsd_short       = "http://www.w3.org/2001/XMLSchema#short";
        const char* xsd_int         = "http://www.w3.org/2001/XMLSchema#int";
        const char* xsd_long        = "http://www.w3.org/2001/XMLSchema#long";
        const char* xsd_unsignedByte        = "http://www.w3.org/2001/XMLSchema#unsignedByte";
        const char* xsd_unsignedShort       = "http://www.w3.org/2001/XMLSchema#unsignedShort";
        const char* xsd_unsignedInt         = "http://www.w3.org/2001/XMLSchema#unsignedInt";
        const char* xsd_unsignedLong        = "http://www.w3.org/2001/XMLSchema#unsignedLong";
        const char* xsd_positiveInteger     = "http://www.w3.org/2001/XMLSchema#positiveInteger";
        const char* xsd_nonNegativeInteger  = "http://www.w3.org/2001/XMLSchema#nonNegativeInteger";
        const char* xsd_negativeInteger     = "http://www.w3.org/2001/XMLSchema#negativeInteger";
        const char* xsd_nonPositiveInteger  = "http://www.w3.org/2001/XMLSchema#nonPositiveInteger";
        const char* xsd_hexBinary           = "http://www.w3.org/2001/XMLSchema#hexBinary";
        const char* xsd_base64Binary        = "http://www.w3.org/2001/XMLSchema#base64Binary";
        const char* xsd_anyURI              = "http://www.w3.org/2001/XMLSchema#anyURI";
        const char* xsd_language            = "http://www.w3.org/2001/XMLSchema#language";
        const char* xsd_normalizedString    = "http://www.w3.org/2001/XMLSchema#normalizedString";
        const char* xsd_token               = "http://www.w3.org/2001/XMLSchema#token";
        // some more related to XML


    } // namespace iri
    
    
}